﻿#include "framework.h"
#include <WS2tcpip.h>
#include "Client.h"
#include "Const.h"
#include "Resource.h"

// 变量声明
TCHAR gUsername[USERNAME_BUF_LEN];  // 用户名

// 函数声明
static void Client_Delete(Client*);
static ResultCode Client_AddFriend(Client*, LPCTSTR);
static void Client_AllowAddFriend(Client*, BOOL, const AddFriendRequest*);
static void Client_Chat(Client*, const ChatInfo*);
static void Client_HandleAddFriendRequest(Client*, const AddFriendRequest*);
static void Client_HandleAddFriendResult(Client*, const AddFriendResult*);
static void Client_HandleChat(Client*, const ChatInfo*);
static void Client_HandleFriendLogoff(Client*, const LogoffRequest*);
static void Client_HandleFriendLogon(Client*, const FriendLogon*);
static void Client_HandleLogonResult(Client*, const LogonResult*);
static void Client_HandleRegResult(Client*, const RegResult*);
static void Client_Logoff(Client*);
static void Client_Logon(Client*, LPCTSTR, LPCTSTR);
static void Client_OnGetMessageFromServer(Client*, const ImProtocol*);
static DWORD WINAPI Client_ReceiveData(LPVOID);
static void Client_Reg(Client*, LPCTSTR, LPCTSTR, int);
static void Client_SendData(Client*, const void*, int);

/**
 * 创建客户端。
 * @param callback  回调
 * @return 客户端
 */
Client* New_Client(HWND callback) {
    WORD version = MAKEWORD(2, 2);
    WSADATA wsaData = { 0 };
    SOCKADDR_IN serverAddr = { AF_INET };
    Client* client = (Client*)calloc(1, sizeof(Client));
    HANDLE threadHandle = NULL;
    int result = SOCKET_ERROR;

    // 初始化网络
    WSAStartup(version, &wsaData);

    // 初始化数据
    client->mCallback = callback;
    gUsername[0] = '\0';
    client->mSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    // 初始化函数
    client->deleteSelf = Client_Delete;
    client->addFriend = Client_AddFriend;
    client->allowAddFriend = Client_AllowAddFriend;
    client->chat = Client_Chat;
    client->logoff = Client_Logoff;
    client->logon = Client_Logon;
    client->reg = Client_Reg;

    // 连接服务器
    serverAddr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &serverAddr.sin_addr);
    result = connect(client->mSocket, (const SOCKADDR*)&serverAddr, sizeof(SOCKADDR_IN));
    if (result == SOCKET_ERROR) {
        return NULL;
    }

    // 开启接收数据的线程
    threadHandle = CreateThread(NULL, 0, Client_ReceiveData, client, 0, NULL);
    CloseHandle(threadHandle);

    return client;
}

/**
 * 删除客户端。
 * @param client    客户端
 */
static void Client_Delete(Client* client) {
    // 离线
    Client_Logoff(client);

    // 延迟一下，防止数据还没发送完成就关闭网络
    Sleep(100);

    // 关闭
    closesocket(client->mSocket);
    free(client);

    // 反初始化网络
    WSACleanup();
}

/**
 * 添加好友。
 * @param client        客户端
 * @param friendName    好友名字
 * @return 结果
 */
static ResultCode Client_AddFriend(Client* client, LPCTSTR friendName) {
    AddFriendRequest* request = NULL;

    // 检查是否超过好友上限
    if (client->mFriendCount > MAX_USER_COUNT) {
        return TOO_MANY_FRIENDS;
    }

    // 检查好友名
    if (_tcscmp(friendName, gUsername) == 0) {
        return FRIEND_NAME_EQUAL_SELF_NAME;
    }

    // 发送添加好友请求
    request = New_AddFriendRequest(friendName, client->mIconIndex, gUsername);
    Client_SendData(client, request, sizeof(AddFriendRequest));

    return SUCCEEDED;
}

/**
 * 允许或拒绝添加好友。
 * @param client    客户端
 * @param allow     允许则为 TRUE，否则为 FALSE
 * @param request   添加好友请求
 */
static void Client_AllowAddFriend(Client* client, BOOL allow, const AddFriendRequest* request) {
    // 发送结果
    AddFriendResult* addFriendResult = New_AddFriendResult(client->mIconIndex, request->mDstUsername, 
        allow ? ADD_FRIEND_ALLOW: ADD_FRIEND_NOT_ALLOW, request->mSrcUsername);
    Client_SendData(client, addFriendResult, sizeof(AddFriendResult));
    free(addFriendResult);

    // 如果允许，则好友数加1
    if (allow) {
        client->mFriendCount++;
    }
}

/**
 * 聊天。
 * @param client    客户端
 * @param chatInfo  聊天信息
 */
static void Client_Chat(Client* client, const ChatInfo* chatInfo) {
    Client_SendData(client, chatInfo, sizeof(ChatInfo));
}

/**
 * 处理添加好友请求。
 * @param client    客户端
 * @param request   添加好友请求
 */
static void Client_HandleAddFriendRequest(Client* client, const AddFriendRequest* request) {
    SendMessage(client->mCallback, UM_GET_ADD_FRIEND_REQUEST, (WPARAM)request, 0);
}

/**
 * 处理添加好友结果。
 * @param client    客户端
 * @param result    添加好友结果
 */
static void Client_HandleAddFriendResult(Client* client, const AddFriendResult* result) {
    if (result->mResultCode == ADD_FRIEND_ALLOW && result->mDstIconIndex != -1) {
        client->mFriendCount++;
    }

    SendMessage(client->mCallback, UM_GET_ADD_FRIEND_RESULT, (WPARAM)result, 0);
}

/**
 * 处理聊天。
 * @param client    客户端
 * @param chatInfo  聊天信息
 */
static void Client_HandleChat(Client* client, const ChatInfo* chatInfo) {
    SendMessage(client->mCallback, UM_CHAT_S2C, (WPARAM)chatInfo, 0);
}

/**
 * 处理其他用户的离线请求。
 * @param client    客户端
 * @param request   其他用户的离线请求
 */
static void Client_HandleFriendLogoff(Client* client, const LogoffRequest* request) {
    SendMessage(client->mCallback, UM_FRIEND_LOGOFF, (WPARAM)request, 0);
}

/**
 * 处理其他用户的登录消息。
 * @param client        客户端
 * @param friendLogon   其他用户的登录信息
 */
static void Client_HandleFriendLogon(Client* client, const FriendLogon* friendLogon) {
    SendMessage(client->mCallback, UM_FRIEND_LOGON, (WPARAM)friendLogon, 0);
}

/**
 * 处理登录结果。
 * @param client    客户端
 * @param result    登录结果
 */
static void Client_HandleLogonResult(Client* client, const LogonResult* result) {
    SendMessage(client->mCallback, UM_GET_LOGON_RESULT, (WPARAM)result, 0);

    if (result->mResultCode == LOGON_SUCCEEDED) {
        client->mIconIndex = result->mIconIndex;
        client->mFriendCount = result->mFriendCount;
    } else {
        gUsername[0] = '\0';
    }
}

/**
 * 处理注册结果。
 * @param client    客户端
 * @param result    注册结果
 */
static void Client_HandleRegResult(Client* client, const RegResult* result) {
    SendMessage(client->mCallback, UM_GET_REG_RESULT, (WPARAM)result, 0);

    if (result->mResultCode != REG_SUCCEEDED) {
        gUsername[0] = '\0';
    }
}

/**
 * 离线。
 * @param client    客户端
 */
static void Client_Logoff(Client* client) {
    LogoffRequest* request = NULL;

    if (gUsername[0] == '\0') {
        return;
    }

    request = New_LogoffRequest(gUsername);
    Client_SendData(client, request, sizeof(LogoffRequest));
    free(request);

    gUsername[0] = '\0';
}

/**
 * 登录。
 * @param client    客户端
 * @param username  用户名
 * @param password  密码
 */
static void Client_Logon(Client* client, LPCTSTR username, LPCTSTR password) {
    // 发送登录请求
    LogonRequest* request = New_LogonRequest(password, username);
    Client_SendData(client, request, sizeof(LogonRequest));
    free(request);

    // 保存用户名
    _tcscpy_s(gUsername, USERNAME_BUF_LEN, username);
}

/**
 * 处理服务器发送来的数据。
 * @param client    客户端
 * @param pro       协议
 */
static void Client_OnGetMessageFromServer(Client* client, const ImProtocol* pro) {
    // 根据协议类型做相应处理
    switch (pro->mType) {
        case ADD_FRIEND_REQUEST:
            Client_HandleAddFriendRequest(client, (AddFriendRequest*)pro);
            break;
        case ADD_FRIEND_RESULT:
            Client_HandleAddFriendResult(client, (AddFriendResult*)pro);
            break;
        case CHAT:
            Client_HandleChat(client, (ChatInfo*)pro);
            break;
        case FRIEND_LOGON:
            Client_HandleFriendLogon(client, (FriendLogon*)pro);
            break;
        case LOGOFF:
            Client_HandleFriendLogoff(client, (LogoffRequest*)pro);
            break;
        case LOGON:
            Client_HandleLogonResult(client, (LogonResult*)pro);
            break;
        case REG:
            Client_HandleRegResult(client, (RegResult*)pro);
            break;
    }
}

/**
 * 接收数据。
 * @param param 客户端
 * @return 总是零
 */
static DWORD WINAPI Client_ReceiveData(LPVOID param) {
    Client* client = (Client*)param;    // 客户端
    int dataSize = 0;                   // 数据长度
    char* buffer = NULL;                // 接收数据的缓存
    int read = 1;                       // 接收到的数据长度
    ImProtocol* pro = NULL;             // 协议

    while (read > 0) {
        // 接收数据长度
        read = recv(client->mSocket, (char*)&dataSize, sizeof(dataSize), 0);
        if (read == SOCKET_ERROR) {
            break;
        }

        // 接收数据
        buffer = (char*)calloc(1, dataSize);
        read = recv(client->mSocket, buffer, dataSize, 0);
        if (read <= 0) {
            free(buffer);
            break;
        }

        // 将数据转换成协议
        pro = (ImProtocol*)buffer;

        // 处理协议
        Client_OnGetMessageFromServer(client, pro);

        // 释放缓存
        free(buffer);
    }

    return 0;
}

/**
 * 注册。
 * @param client    客户端
 * @param username  用户名
 * @param password  密码
 * @param iconIndex 头像索引
 */
static void Client_Reg(Client* client, LPCTSTR username, LPCTSTR password, int iconIndex) {
    // 发送注册请求
    RegRequest* request = New_RegRequest(iconIndex, password, username);
    Client_SendData(client, request, sizeof(RegRequest));
    free(request);

    // 保存用户名和头像索引
    _tcscpy_s(gUsername, USERNAME_BUF_LEN, username);
    client->mIconIndex = iconIndex;
}

/**
 * 发送数据。
 * @param client    客户端
 * @param pro       协议
 * @param dataSize  数据长度
 */
static void Client_SendData(Client* client, const void* pro, int dataSize) {
    // 发送数据长度
    send(client->mSocket, (const char*)&dataSize, sizeof(dataSize), 0);

    // 发送数据
    send(client->mSocket, pro, dataSize, 0);

    // 防止发送过快导致服务器无法响应
    Sleep(100);
}
